using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Data;
using System.IO;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
using DeliciousAPI;
using DeliciousToolbar.Properties;
using Timer=System.Timers.Timer;
using System.Net;

namespace DeliciousToolbar
{
    [ComVisible(true)]
    public partial class Toolbar
    {
        private DeliciousAPI.RequestService ApiRequestService;
        private PostCollection Posts;
        protected List<RecentBookmark> RecentBookmarks;
        private List<Button> RecentBookmarkButtons;
        private System.Timers.Timer BookmarkRefreshTimer;
        private static bool AuthErrorOccured = false;
        private readonly TimeSpan CacheDuration = new TimeSpan(0, 5, 0);

        #region Properties
        private bool CredentialsEntered
        {
            get
            {
                if (Settings.Default.Username == null
                    || Settings.Default.Password == null
                    || Settings.Default.Username.Trim().Length == 0
                    || Settings.Default.Password.Trim().Length == 0)
                    return false;
                return true;
            }
        }
        #endregion

        #region Constructor
        public Toolbar()
        {
            InitializeComponent();
            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

            this.BookmarkRefreshTimer = new Timer();
            this.BookmarkRefreshTimer.Interval = new TimeSpan(0, 1, 0).TotalMilliseconds;
            this.BookmarkRefreshTimer.Elapsed += new System.Timers.ElapsedEventHandler(BookmarkRefreshTimer_Elapsed);

            Settings.Default.Reload();
            if (!CredentialsEntered)
            {
                this.AuthLink.Visible = true;
                this.LogOffBtn.Visible = false;
                this.ShowBookmarksBtn.Enabled = false;
                this.AddBookmarkBtn.Enabled = false;
            }
            else
            {
                this.LogOffBtn.Visible = true;
                this.AuthLink.Visible = false;
                ApiRequestService = new RequestService(Settings.Default.Username, Settings.Default.Password, true, CacheDuration);
                this.BookmarkRefreshTimer.Start();
            }

            Posts = new PostCollection();
            RecentBookmarks = new List<RecentBookmark>();
            RecentBookmarkButtons = new List<Button>();
        }
        #endregion

        #region Default Exception Handler
        void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            MessageBox.Show(string.Format("An Unrecoverable error has occured.\n\n{0}\n\n{1}", e.Exception.Message, e.Exception.StackTrace),
                "Delicious Toolbar - Unrecoverable Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            this.Dispose();
        }
        #endregion

        #region OnLoad
        private void Toolbar_Load(object sender, EventArgs e)
        {
            this.Explorer.OnQuit += new SHDocVw.DWebBrowserEvents2_OnQuitEventHandler(Explorer_OnQuit);

            if (!string.IsNullOrEmpty(Settings.Default.Bookmarks))
            {
                XmlSerializer Ser = new XmlSerializer(typeof(PostCollection));
                StringReader sr = new StringReader(Settings.Default.Bookmarks);
                Posts = (PostCollection)Ser.Deserialize(sr);
            }

            if (!string.IsNullOrEmpty(Settings.Default.RecentBookmarks))
            {
                XmlSerializer Ser = new XmlSerializer(typeof(List<RecentBookmark>));
                StringReader sr = new StringReader(Settings.Default.RecentBookmarks);
                this.RecentBookmarks = (List<RecentBookmark>)Ser.Deserialize(sr);
            }

            LoadRecentBookmarks();
        }
        #endregion

        #region Load Recent Bookmarks
        private void LoadRecentBookmarks()
        {
            //first clear old buttons
            
            for (int i = this.Controls.Count - 1; i >= 0; i--)
            {
                if (this.Controls[i] is Button && ((Button)this.Controls[i]).Name == "RecentButton")
                    this.Controls.RemoveAt(i);
            }
            RecentBookmarkButtons.Clear();
            
            foreach (RecentBookmark BM in RecentBookmarks)
            {
                string BtnTxt = BM.Description;
                if (BtnTxt.Length > 25)
                    BtnTxt = BtnTxt.Substring(0, 25);

                Button B = new Button();
                B.Name = "RecentButton";
                B.ImageAlign = ContentAlignment.MiddleLeft;
                B.TextImageRelation = TextImageRelation.ImageBeforeText;
                B.Text = BtnTxt;
                B.Width = BtnTxt.Length * 5 + 45;
                if (BM.Favicon != null)
                {
                    B.Image = BM.Favicon;
                }
                RecentBookmarkButtons.Insert(0, B);
            }

            int ButtonXPos = this.LogOffBtn.Location.X + this.LogOffBtn.Width + 20;

            foreach (Button B in RecentBookmarkButtons)
            {
                if (ButtonXPos + B.Width < this.Location.X + this.Width)
                {
                    B.Location = new Point(ButtonXPos, 2);
                    this.Controls.Add(B);
                    ButtonXPos += B.Width + 5;
                }
                else
                    break;
            }

            FaviconFetcherAsync FavFetcher = new FaviconFetcherAsync();
            FavFetcher.FaviconFetchCompleted += new FaviconFetchCompletedEventHandler(FavFetcher_FaviconFetchCompleted);
            FavFetcher.BeginFetchFavicons(RecentBookmarks);
        }

        private void FavFetcher_FaviconFetchCompleted(object sender, FaviconFetchCompletedEventArgs e)
        {
            foreach (RecentBookmark BM in e.Result)
            {
                foreach (Button B in RecentBookmarkButtons)
                {
                    if (B.Image == null && B.Text == BM.Description && BM.Favicon != null)
                    {
                        B.Image = BM.Favicon;
                    }
                }
            }
        }
        #endregion
        
        #region OnQuit
        void Explorer_OnQuit()
        {
            XmlSerializer Ser = new XmlSerializer(typeof(PostCollection));
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            Ser.Serialize(sw, Posts);
            Settings.Default.Bookmarks = sb.ToString();

            Ser = new XmlSerializer(typeof(List<RecentBookmark>));
            sb = new StringBuilder();
            sw = new StringWriter(sb);
            Ser.Serialize(sw, RecentBookmarks);
            Settings.Default.RecentBookmarks = sb.ToString();

            if (!Settings.Default.SaveAuth)
            {
                Settings.Default.Username = String.Empty;
                Settings.Default.Password = String.Empty;
            }
            Settings.Default.Save();
        }
        #endregion

        #region Update Bookmarks
        void BookmarkRefreshTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateBookmarks_Async), null);
        }

        private void UpdateBookmarks_Async(object state)
        {
            if (ValidateAuth())
            {
                try
                {
                    string RootPath = Application.CommonAppDataPath;
                    string LastUpdateFile = Path.Combine(RootPath, "LastDeliciousUpdate");

                    try
                    {
                        DateTime LastUpdate = File.GetLastWriteTime(LastUpdateFile);
                        // if the file last write time was less than 5 minutes ago,
                        // do nothing.
                        if (LastUpdate.AddMinutes(5) >= DateTime.Now)
                            return;
                        else
                            File.SetLastWriteTime(LastUpdateFile, DateTime.Now);

                    }
                    catch (DirectoryNotFoundException)
                    {
                        Directory.CreateDirectory(RootPath);
                        goto Update;
                    }
                    catch (FileNotFoundException)
                    {
                        File.Create(LastUpdateFile);
                        File.SetAttributes(LastUpdateFile, FileAttributes.Hidden | FileAttributes.NotContentIndexed);
                        goto Update;
                    }

                Update:
                    PostCollection Result = new PostCollection();
                    if (ApiRequestService.GetAllPosts(Posts.Date, out Result))
                    {
                        Posts = Result;
                    }
                }
                catch (SecurityException)
                {
                    HandleAuthError();
                }
                catch (Exception e)
                {
                    MessageBox.Show(string.Format("An Unrecoverable error has occured.\n\n{0}\n\n{1}", e.Message, e.StackTrace),
                                    "Delicious Toolbar - Unrecoverable Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        #endregion

        #region Add Post
        private void AddBookmarkBtn_Click(object sender, EventArgs e)
        {
            if (ValidateAuth())
            {
                AddBookmarkForm AddForm = new AddBookmarkForm(Explorer.LocationURL, Explorer.LocationName);
                if (AddForm.ShowDialog() == DialogResult.OK)
                {
                    Posts.Posts.Add(AddForm.NewPost);
                    ThreadPool.QueueUserWorkItem(new WaitCallback(AddPost_Async), AddForm.NewPost);
                }
            }
        }

        private void AddPost_Async(object state)
        {
            try
            {
                if (ValidateAuth())
                {
                    ApiRequestService.AddPost(state as Post);
                }
            }
            catch (SecurityException)
            {
                HandleAuthError();
            }
        }
        #endregion

        #region Show Bookmarks
        private void ShowBookmarksBtn_Click(object sender, EventArgs e)
        {
            Button ShowBookmarkButton = (Button)sender;
            using (BookmarkView BookmarkForm = new BookmarkView(Posts))
            {
                BookmarkForm.StartPosition = FormStartPosition.Manual;
                BookmarkForm.Location = new Point(Explorer.Left + this.Left + ShowBookmarkButton.Left, Explorer.Top + this.Top + ShowBookmarkButton.Top + 50);

                if (BookmarkForm.ShowDialog() == DialogResult.OK)
                {
                    object o = null;
                    this.Explorer.Navigate(BookmarkForm.UrlSelected, ref o, ref o, ref o, ref o);

                    foreach (Post P in this.Posts.Posts)
                    {
                        if (P.Href == BookmarkForm.UrlSelected)
                        {
                            RecentBookmarks.Insert(0, new RecentBookmark(BookmarkForm.UrlSelected, P.Description));
                            if (RecentBookmarks.Count > 10)
                                RecentBookmarks.RemoveAt(RecentBookmarks.Count - 1);

                            LoadRecentBookmarks();
                        }
                    }
                }
            }
        }
        #endregion

        #region Authenticate
        private bool ValidateAuth()
        {
            if (AuthErrorOccured)
            {
                this.LogOffBtn.Visible = false;
                this.AuthLink.Visible = true;
                this.ShowBookmarksBtn.Enabled = false;
                this.AddBookmarkBtn.Enabled = false;
                AuthErrorOccured = false;
                return false;
            }

            return true;
        }

        private void AuthLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            LinkLabel AuthenticateBtn = (LinkLabel)sender;
            using (CredentialsForm CredForm = new CredentialsForm())
            {
                CredForm.StartPosition = FormStartPosition.Manual;
                CredForm.Location = new Point(Explorer.Left + this.Left + AuthenticateBtn.Left, Explorer.Top + this.Top + AuthenticateBtn.Top + 50);
                if (CredForm.ShowDialog(this) == DialogResult.OK)
                {
                    this.AuthLink.Visible = false;
                    this.LogOffBtn.Visible = true;
                    this.ShowBookmarksBtn.Enabled = true;
                    this.AddBookmarkBtn.Enabled = true;
                    ApiRequestService = new RequestService(Settings.Default.Username, Settings.Default.Password, true, CacheDuration);
                    this.BookmarkRefreshTimer.Start();
                }
            }
        }
        #endregion

        #region Handle Auth Error
        private void HandleAuthError()
        {
            BookmarkRefreshTimer.Stop();
            AuthErrorOccured = true;
        }
        #endregion

        #region Delicious Search
        private void SearchBtn_Click(object sender, EventArgs e)
        {
            string SearchUrl = "";
            if (this.TagRB.Checked)
                SearchUrl = DeliciousAPI.Search.SearchTagUrl(this.SearchTermTB.Text);
            else if (this.PopularRB.Checked)
                SearchUrl = DeliciousAPI.Search.SearchPopularUrl(this.SearchTermTB.Text);

            object o = null;
            this.Explorer.Navigate(SearchUrl, ref o, ref o, ref o, ref o);
        }

        private void SearchTermTB_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                this.SearchBtn.PerformClick();
        }

        private void DeliciousBtn_Click(object sender, EventArgs e)
        {
            object o = null;
            this.Explorer.Navigate(DeliciousAPI.Search.DeliciousRoot, ref o, ref o, ref o, ref o);
        }
        #endregion

        #region Logoff
        private void LogOffBtn_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Settings.Default.Username = String.Empty;
            Settings.Default.Password = String.Empty;
            Settings.Default.Save();
            this.ShowBookmarksBtn.Enabled = false;
            this.AddBookmarkBtn.Enabled = false;
            this.LogOffBtn.Visible = false;
            this.AuthLink.Visible = true;
        }
        #endregion

    }

}